// src/auth/api-key.guard.ts
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, ForbiddenException } from '@nestjs/common';
import { createHmac } from 'crypto';
import { ApiKeyService } from '@/modules/api-key/api-key.service';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private apiKeyService: ApiKeyService) { }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const headers = request.headers;

    // 1. 从请求头获取必要参数
    const apiKey = headers['x-api-key'];
    const timestamp = headers['x-timestamp']; // 时间戳（防止重放攻击）
    const nonce = headers['x-nonce']; // 随机字符串（防止重放攻击）
    const signature = headers['x-signature']; // 客户端生成的签名

    // 校验参数完整性
    if (!apiKey || !timestamp || !nonce || !signature) {
      throw new UnauthorizedException('Missing API key parameters');
    }

    // 2. 验证时间戳（防止过期请求，例如限制5分钟内有效）
    const now = Date.now();
    const requestTime = parseInt(timestamp, 10);
    if (Math.abs(now - requestTime) > 5 * 60 * 1000) {
      throw new UnauthorizedException('Request expired (timestamp invalid)');
    }

    // 3. 查询 API Key 对应的配置和 Secret
    const apiKeyEntity = await this.apiKeyService.findByApiKey(apiKey);
    if (!apiKeyEntity) {
      throw new UnauthorizedException('Invalid API key');
    }

    // 4. 验证是否过期
    if (apiKeyEntity.expiresAt && apiKeyEntity.expiresAt < new Date()) {
      throw new UnauthorizedException('API key expired');
    }

    // 5. 验证签名（核心步骤）
    const signatureValid = this.verifySignature(
      request.method,
      request.originalUrl,
      timestamp,
      nonce,
      apiKeyEntity.apiSecretHash, // 数据库中的加密 Secret
      signature,
    );
    if (!signatureValid) {
      throw new UnauthorizedException('Invalid signature');
    }

    // 6. 验证模块权限（获取当前控制器的模块名）
    const controller = context.getClass();
    const moduleName = Reflect.getMetadata('moduleName', controller);
    if (!moduleName || !apiKeyEntity.allowedModules.includes(moduleName)) {
      throw new ForbiddenException(`API key cannot access module: ${moduleName}`);
    }

    // 7. 附加信息到请求对象，供后续拦截器使用
    request.apiKey = apiKey;
    request.apiKeyEntity = apiKeyEntity;
    return true;
  }

  // 验证签名是否合法
  private verifySignature(
    method: string,
    url: string,
    timestamp: string,
    nonce: string,
    secretHash: string, // 数据库中的加密 Secret
    clientSignature: string,
  ): boolean {
    // 生成待签名字符串（需与客户端规则一致）
    const signStr = `${method.toUpperCase()}\n${url}\n${timestamp}\n${nonce}`;

    // 用 Secret 生成 HMAC-SHA256 签名（客户端逻辑与此相同）
    const serverSignature = createHmac('sha256', secretHash)
      .update(signStr)
      .digest('hex');

    // 对比签名（注意：客户端需用原始 API Secret 签名，服务端用数据库中的 hash 验证）
    return serverSignature === clientSignature;
  }
}

// 示例代码（客户端）：
// const axios = require('axios');
// const { createHmac } = require('crypto');

// // 客户端保存的 API Key 和 Secret（仅生成时获取）
// const API_KEY = 'AKIAEXAMPLE1234567890';
// const API_SECRET = 'a1b2c3d4e5f6...'; // 客户端需妥善保存

// // 生成签名
// const method = 'GET';
// const url = '/user';
// const timestamp = Date.now().toString();
// const nonce = Math.random().toString(36).substring(2, 15); // 随机字符串

// // 待签名字符串（与服务端规则一致）
// const signStr = `${method.toUpperCase()}\n${url}\n${timestamp}\n${nonce}`;
// // 用 API Secret 生成签名
// const signature = createHmac('sha256', API_SECRET)
//   .update(signStr)
//   .digest('hex');

// // 发送请求
// axios.get('http://localhost:3000/user', {
//   headers: {
//     'x-api-key': API_KEY,
//     'x-timestamp': timestamp,
//     'x-nonce': nonce,
//     'x-signature': signature,
//   },
// })
//   .then(res => console.log(res.data))
//   .catch(err => console.error(err.response.data));